package com.whitehatsec.sentineldataexchange;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.FileOutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import org.jasypt.util.text.BasicTextEncryptor;

/**
 * This class is used to login to the Sentinel API. Upon successful login, the APID will be available
 * as an instance variable in this class, accessible with the getAPID() method.
 *
 * @author August Detlefsen [augustd@codemagi.com]
 */
public class SentinelLogin {

	private transient String apid;
	private static final transient String PASSWORD = "Us#OntUsF`eeRMyr";

	public SentinelLogin(Properties properties) throws Exception {

		HttpURLConnection urlConn;
		DataOutputStream printout = null;
		BufferedReader input = null;

		try {
			//create the login URL
			String loginURL = properties.getProperty("WHITEHAT_LOGIN_API_URL");
			loginURL = String.format(loginURL, properties.getProperty("WHITEHAT_USERNAME"));
			URL url = new URL(loginURL);

			//if we are dealing with http, DO NOT follow redirects
			HttpURLConnection.setFollowRedirects(false);

			// Open URL connection
			urlConn = (HttpURLConnection)url.openConnection();

			//make sure we are using a POST request
			urlConn.setRequestMethod("POST");

			// Let the run-time system (RTS) know that we want input and output
			urlConn.setDoInput(true);
			urlConn.setDoOutput(true);

			// No caching, we want the real thing.
			urlConn.setUseCaches(false);

			// Specify the content type.
			urlConn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");

			// Send POST
			printout = new DataOutputStream(urlConn.getOutputStream());
			
			printout.writeBytes("password=");
			printout.writeBytes(URLEncoder.encode(getApiPassword(properties), "UTF-8"));

			printout.flush();

			//look for the authentication cookie
			Map<String, List<String>> headers = urlConn.getHeaderFields();
			List<String> cookies = headers.get("Set-Cookie");

			for (String cookie : cookies) {
				cookie = cookie.substring(0, cookie.indexOf(";"));
				String cookieName = cookie.substring(0, cookie.indexOf("="));
				if ("APID".equals(cookieName)) {
					apid = cookie.substring(cookie.indexOf("=") + 1, cookie.length());
					//System.out.println("APID: " + apid); //For debug only!
					break;
				}
			}

		} finally {
			//clean up
			Utils.closeStream(printout);
			Utils.closeStream(input);

		}

	}

	/**
	 * Gets the decrypted password from the properties file. If the password is in plain text
	 * it will be encrypted, stored and wiped from the properties file.
	 *
	 * @param properties
	 * @return
	 */
	private String getApiPassword(Properties properties) {
		String plaintextPassword = properties.getProperty("WHITEHAT_PASSWORD");

		BasicTextEncryptor textEncryptor = new BasicTextEncryptor();
		textEncryptor.setPassword(PASSWORD);

		if (!Utils.isEmpty(plaintextPassword)) {
			//encrypt the new password
			String encryptedPassword = textEncryptor.encrypt(plaintextPassword);

			//store the encrypted password, and wipe the plaintext
			properties.setProperty("WHITEHAT_PASSWORD_ENCRYPTED", encryptedPassword);
			properties.remove("WHITEHAT_PASSWORD");

			//save the new properties file
			FileOutputStream out = null;
			try {
				//determine the file to write to
				String propertiesFilename = properties.getProperty("PROPERTIES_FILENAME", "runtime.properties");

				out = new FileOutputStream(propertiesFilename);
				properties.store(out, "");
			} catch (Exception e) {
				System.err.println("Unable to overwrite properties file");
				e.printStackTrace(System.err);
			} finally {
				Utils.closeStream(out);
			}

			return plaintextPassword;
		}

		//otherwise, the password is already store encrypted
		String encryptedPassword = properties.getProperty("WHITEHAT_PASSWORD_ENCRYPTED");
		return textEncryptor.decrypt(encryptedPassword);
	}

	public String getAPID() {
		return apid;
	}

	/**
	 * Returns the APID cookie as a java.util.Map suitable for adding to an API request.
	 * @return
	 */
	public Map<String, String> getCookies() {
		Map<String, String> output = new HashMap<String, String>(1);
		output.put("APID", apid);
		return output;
	}
}
